﻿using FundHelper.Model;
using MysToolCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace FundHelper.Utility
{
    static partial class PrepareData
    {
        public static IEnumerable<(DateTime date, double value)> ToTuple<T>(this IEnumerable<T> list) where T : ICanToTuple
        {
            return list.Select(a => a.ToTuple());
        }
        public static IEnumerable<Quote> ToQuote<T>(this IEnumerable<T> list) where T : ICanToTuple
        {
            return list.Select(a => a.ToQuote());
        }
        public static double Normalization<T>(T max, T min, T value) where T : INumber<T>
        {
            T during = max - min;
            value = (value - min) / during;
            return double.CreateChecked(value);
        }
        public static double Normalization<T>(T[] values, T value) where T : INumber<T>
        {
            var max = values.Max();
            var min = values.Min();
            if(max is null||min is null)
                throw new ArgumentNullException("值数组异常，无法获取最大值或最小值");
            return Normalization(max, min, value);
        }

        public static double Normalization<T>(List<T> values, T value) where T: INumber<T>
        {
            var max = values.Max();
            var min = values.Min();
            if (max is null || min is null)
                throw new ArgumentNullException("值数组异常，无法获取最大值或最小值");
            return Normalization(max, min, value);
        }
        public static T Standardization<T>(IEnumerable<T> values, T value) where T : INumber<T>
        {
           var t= values.CalculateStatistic();
            return Standardization(t, value);
        }
        public static T Standardization<T>(Statistic<T> t, T value)where T:INumber<T>
        {
            return T.CreateChecked((double.CreateChecked(value) - t.Mean) / Math.Sqrt(t.Variance));
        }

        public static IEnumerable<String[]> GetItemData(this List<Dictionary<string, string[]>> data,IndicatorEnum item)
        {
            return data.Select(x => x[item.ToString()]);
        }

        /// <summary>
        /// 将 source 集合中的元素复制到 des 集合中，最多复制 count 个元素。
        /// </summary>
        /// <param name="source">源集合</param>
        /// <param name="des">目标集合，必须是可以修改的集合类型，如 List<string[]> 或数组</param>
        /// <param name="count">要复制的元素数量</param>
        public static void CopyTo(this IEnumerable<string[]> source, IEnumerable<string[]> des, int count)
        {
            if (source == null) throw new ArgumentNullException(nameof(source));
            if (des == null) throw new ArgumentNullException(nameof(des));
            if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "Count cannot be negative.");

            // 如果目标集合长度小于 count，抛出异常或扩展目标集合（这里选择抛出异常）
            if (des.Count() < count)
            {
                throw new ArgumentException("Destination array is not long enough to copy all items.", nameof(des));
            }

            int index = 0;
            foreach (var item in source)
            {
                if (index >= count) break; // 达到最大复制数量后停止
                Array.Copy(item, des.ElementAt(index), item.Count());
                index++;
            }
        }


        public static void Replace(this IEnumerable<Dictionary<string,string[]>> orgin, IEnumerable<string[]> newData, IndicatorEnum item)
        {
            int idx = 0;
            foreach (var line in orgin)
            {
                line[item.ToString()]=newData.ElementAt(idx);
                idx++;
            }
        }
        /// <summary>
        /// 求统计学五值
        /// </summary>
        /// <param name="values"></param>
        /// <param name="period"></param>
        /// <returns>最大值，最小值，中位数，方差，平均值</returns>
        /// <exception cref="ArgumentException"></exception>
        public static Statistic<T> CalculateStatistic<T>(this IEnumerable<T> values, int period) where T:INumber<T>
        {
            if(values.Count()<period)
                throw new ArgumentException("Not enough data points for the specified FiveNum period.");
            var nums=values.Take(period);
            var avg = nums.ToArray().AsSpan().Average();
            var max = nums.Max()??T.CreateChecked(double.MinValue);
            var min = nums.Min()?? T.CreateChecked(double.MaxValue);
            var mid= nums.OrderBy(a=>a).Skip(period/2).First();
            //考虑到都是样本方差，所以选择了N-1
            double variance = nums.Sum(a => Math.Pow(double.CreateChecked(a) - avg, 2))/(nums.Count()-1);
            return new Statistic<T>(max, min, mid, variance, avg);
        }
        /// <summary>
        /// 求统计学五值
        /// </summary>
        /// <param name="values"></param>
        /// <param name="period"></param>
        /// <returns>最大值，最小值，中位数，方差，平均值</returns>
        /// <exception cref="ArgumentException"></exception>
        public static Statistic<T> CalculateStatistic<T>(this Span<T> values, int period) where T:INumber<T>
        {
            if (values.Length < period)
                throw new ArgumentException("Not enough data points for the specified FiveNum period.");
            Span<T> nums =values.Slice(0, period);
            var avg = nums.Average();
            var max = nums.Max();
            var min = nums.Min();
            var mid = nums.Median();
            //考虑到都是样本方差，所以选择了N-1
            var variance = nums.Variance();
            return new Statistic<T>(max, min, mid, variance, avg);
        }
        /// <summary>
        /// 求统计学五值
        /// </summary>
        /// <param name="values"></param>
        /// <returns>最大值，最小值，中位数，方差，平均值</returns>
        /// <exception cref="ArgumentException"></exception>
        public static Statistic<T> CalculateStatistic<T>(this IEnumerable<T> values) where T : INumber<T>
        {
            return values.CalculateStatistic<T>(values.Count());
        }
        public static Statistic<T> CalculateStatistic<T>(this Span<T> values) where T : INumber<T>
        {
            return values.CalculateStatistic<T>(values.Length);
        }

    }
}
